api: RETRO-2026-05-12 follow-ups — openapi for cached aggregates + agent_action on 401#24
Merged
Merged
Conversation
…ent_action on 401
Two small fixes flagged in the session retro:
Fix 1: /openapi.json was missing entries for the Wave 4-L cached aggregate
endpoints — GET /api/v1/billing/usage and GET /api/v1/team/summary — that
were already live and tested in production. Agents reading the spec alone
had no machine-readable signal that the cached aggregates existed, so they
were forced to fall back to scanning the full /resources list to compute
the same numbers the dashboard already had pre-aggregated. Added path
entries with response examples, Cache-Control header documentation, and
BillingUsageResponse / TeamSummaryResponse / TeamSummaryResourceCounts /
UsageMetric schemas. New test
TestOpenAPI_CachedAggregateEndpointsDocumented guards both paths +
schemas + the Cache-Control header contract so a future cleanup doesn't
silently drop them.
Fix 2: middleware.RequireAuth was emitting the bare three-key 401 shape
{ok:false, error:"unauthorized"} on every auth rejection. Downstream
handlers that go through respondError already emit the agent_action
sentence for the "unauthorized" code (via codeToAgentAction in
helpers.go), but the middleware bypasses that helper to avoid a circular
import. The fix inlines the same prose + login URL directly in a new
respondUnauthorized helper so an agent inspecting any 401 from this API
gets the same remediation guidance regardless of which layer rejected
the request. Six new test cases in auth_agent_action_test.go cover every
RequireAuth failure path (no header, malformed bearer, invalid JWT,
wrong secret, expired JWT, bearer-only).
Verification after merge:
curl -sS https://api.instanode.dev/api/v1/resources | jq .
should now return:
{
"ok": false,
"error": "unauthorized",
"agent_action": "The user's INSTANODE_TOKEN is invalid or expired...",
"upgrade_url": "https://instanode.dev/login"
}
instead of the prior bare three-key shape.
Test gate: make test-unit — all packages green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two small follow-up fixes flagged in the session retro doc:
Fix 1: OpenAPI documents the cached aggregate endpoints. The Wave 4-L endpoints
GET /api/v1/billing/usageandGET /api/v1/team/summarywere already live and tested in production, but/openapi.jsonhad no path entries for them. Agents reading the spec alone had no machine-readable signal that the cached aggregates existed, so they were forced to scan the full/resourceslist to compute the same numbers the dashboard already had pre-aggregated. This PR adds:BillingUsageResponse,TeamSummaryResponse,TeamSummaryResourceCounts,UsageMetricschemasTestOpenAPI_CachedAggregateEndpointsDocumentedguards both paths, schemas, and the Cache-Control header contractFix 2: agent_action surfaces on every middleware 401.
middleware.RequireAuthwas emitting the bare three-key 401 shape{ok:false, error:"unauthorized"}on auth rejection. Downstream handlers that go throughrespondErroralready emit theagent_actionsentence for the "unauthorized" code (viacodeToAgentActionin helpers.go), but middleware bypasses that helper to avoid a circular import. The fix inlines the same prose + login URL directly in a newrespondUnauthorizedhelper so an agent inspecting any 401 from this API gets the same remediation guidance regardless of which layer rejected the request. Six new test cases inauth_agent_action_test.gocover every RequireAuth failure path (no header, malformed bearer, invalid JWT, wrong secret, expired JWT, bearer-only).Test plan
make test-unit— all packages green locallycurl -sS https://api.instanode.dev/api/v1/resources | jq .returnsagent_action+upgrade_urlfields (was previously the bare three-key shape)curl -sS https://api.instanode.dev/openapi.json | jq '.paths | keys[] | select(test("billing/usage|team/summary"))'lists both new pathsNotes
agent_actionmatches the "unauthorized" entry inhandlers.codeToAgentActionverbatim, so handler-emitted 401s and middleware-emitted 401s now return identical prose.🤖 Generated with Claude Code